package com.zhudi.algorithm;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class Similary {
    /**
     * 计算两个标签集合的相似度
     * @param str1 集合1
     * @param str2 集合2
     * @return 相似度
     */
    public static Double calculateSimilary(String str1,String str2) {
        //将字符串解析为标签数组
        String[] tags1 = parseTagString(str1);
        String[] tags2 = parseTagString(str2);

        //获取标签的词频统计
        Map<String, Integer> map1 = getTagFrequency(tags1);
        Map<String, Integer> map2 = getTagFrequency(tags2);

        //创建一个HashSet，存放所有的标签，用来创建词频向量
        HashSet<String> allTags = new HashSet<>();
        allTags.addAll(map1.keySet());
        allTags.addAll(map2.keySet());

        //创建两个词频向量
        int[] vector1 = new int[allTags.size()];
        int[] vector2 = new int[allTags.size()];

        int i = 0;
        //遍历所有标签，根据词频填充向量
        for (String tag : allTags) {
            vector1[i] = map1.getOrDefault(tag, 0);
            vector2[i] = map2.getOrDefault(tag, 0);
            i++;
        }

        //计算余弦相似度
        double result = getCosineSimilarity(vector1, vector2);
        return result;
    }

    /**
     * 计算两个字符串的余弦相似度
     * @param map1 字符串1的词频统计集合
     * @param str2 要比较的字符串
     * @return 最后的结果
     */
    public static Double calculateSimilary(Map<String, Integer> map1,String str2) {
        //将字符串解析为标签数组
        String[] tags2 = parseTagString(str2);

        //获取标签的词频统计
        Map<String, Integer> map2 = getTagFrequency(tags2);

        //创建一个HashSet，存放所有的标签，用来创建词频向量
        HashSet<String> allTags = new HashSet<>();
        allTags.addAll(map1.keySet());
        allTags.addAll(map2.keySet());

        //创建两个词频向量
        int[] vector1 = new int[allTags.size()];
        int[] vector2 = new int[allTags.size()];

        int i = 0;
        //遍历所有标签，根据词频填充向量
        for (String tag : allTags) {
            vector1[i] = map1.getOrDefault(tag, 0);
            vector2[i] = map2.getOrDefault(tag, 0);
            i++;
        }

        //计算余弦相似度
        double result = getCosineSimilarity(vector1, vector2);
        return result;
    }

    //解析字符串为标签数组
    public static String[] parseTagString(String str) {
        //如果字符串为空则返回空数组
        if (str==null||str.equals("[]")||"".equals(str)) {
            return new String[0];
        } else {
            //去掉字符串的首尾字符，然后按照逗号分割
            return str.substring(1, str.length() - 1).split(",");
        }
    }

    //统计标签词频
    public static Map<String, Integer> getTagFrequency(String[] tags) {
        //创建一个HashMap用来存放标签词频
        Map<String, Integer> map = new HashMap<>();
        //遍历标签数组
        for (String tag : tags) {
            //去除标签前后的空格
            tag = tag.trim();
            //如果map中没有此标签，则添加到map并计数设为1，否则计数加1
            map.put(tag, map.getOrDefault(tag, 0) + 1);
        }
        //返回标签词频map
        return map;
    }

    //计算余弦相似度
    public static double getCosineSimilarity(int[] vector1, int[] vector2) {
        double dotProduct = 0.0;   //点乘
        double normA = 0.0;        //向量A的模
        double normB = 0.0;        //向量B的模
        //遍历向量
        for (int i = 0; i < vector1.length; i++) {
            //计算点乘和向量模
            dotProduct += vector1[i] * vector2[i];
            normA += Math.pow(vector1[i], 2);
            normB += Math.pow(vector2[i], 2);
        }
        //计算余弦相似度，如果一个向量的模为0（所有元素的值都为0），则返回0
        if(normA == 0.0 || normB == 0.0) {
            return 0.0;
        } else {
            return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
        }

    }
}
